package com.jdbc.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * Copyright (c) 2023-2033 All Rights Reserved
 *
 * JDBC Part4 JDBC 使用优化，封装工具类
 * Description: 通过读取配置文件创建连接池
 *
 * TODO:
 *  1. 方法类内部包含一个连接池 DataSource，用于创建、回收连接。
 *  2. 连接池创建：读取配置文件、加载配置文件、生成连接池通过静态代码块实现
 *  3. 连接池方法：工具类方法定义为静态，方便外部调用，提供创建、回收连接的方法
 *  4. 利用线程本地变量 ThreadLocal 保存连接信息，实现一个线程的多个方法调用获取getConnection()时取到的是同一个 Connection
 *  5. getConnection()：先从 ThreadLocal 获取 Connection，不存在时再通过 DataSource 创建。
 *  6. freeConnection()：从 ThreadLocal 可以获取到 Connection，再执行 connection.close();
 *  7. ThreadLocal 方法： get()、set(connection)、remove()
 *
 * @date: 2023/9/25
 */
public class JdbcUtils {
    // 连接池对象
    private static DataSource dataSource = null;

    // 线程本地变量用于保存当前线程使用的Connection，保证事务执行
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal();

    static {
        // 1. 读取配置文件
        InputStream ips = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");
        Properties properties = new Properties();

        try {
            // 2. 加载配置文件
            properties.load(ips);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        try {
            // 3. 通过配置文件生成连接池
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static Connection getConnection() throws SQLException {
        // 先从线程本地变量获取
        Connection connection = threadLocal.get();

        // 不存在时再从连接池创建
        if (connection == null) {
            connection = dataSource.getConnection();
            threadLocal.set(connection);
        }
        return connection;
    }

    public static void freeConnection() throws SQLException {
        // 直接从本地变量获取
        Connection connection = threadLocal.get();
        if (connection != null) {
            threadLocal.remove();
            connection.setAutoCommit(true);
            // 还给连接池
            connection.close();
        }
    }

}
